package nl.nhl.idp.Views;
import java.io.IOException;

import nl.nhl.idp.R;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class JoystickView extends View 
{
	// =========================================
	// Private Members
	// =========================================
	private double joystickX, joystickY;
	private double stickPressX, stickPressY;
	private int joystickRad;
	private int stickRad;
	private Bitmap background;
	private Bitmap stick;
	private Paint bgPaint;
	private Paint stickPaint;
	private JoystickListener listener;
	private int sensitivity;
	private boolean released = true;
	
	private boolean allowX = true;
	private boolean allowY = true;

	// =========================================
	// Constructors
	// =========================================
	
	public void SetAllowX(boolean x)
	{
		allowX = x;
	}
	
	public void SetAllowY(boolean y)
	{
		allowY = y;
	}
	
	public int GetSensitivity()
	{
		return sensitivity;
	}
	
	public JoystickView(Context context) 
	{
		this(context, null);
	}
	
	public JoystickView(Context context, AttributeSet attrs) 
	{
		this(context, attrs, 0);
	}
	
	public JoystickView(Context context, AttributeSet attrs, int defStyle) 
	{
		super(context, attrs, defStyle);
		int joyRad = 100;
		int sensitivity = 1;
		
		// check if we are using attributes
		if(attrs != null)
		{
			// get typedArray of attributes
			TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VirtualJoystickAttr);
			
			// get the attributes
			joyRad = a.getInt(R.styleable.VirtualJoystickAttr_radius, 80);
			sensitivity = a.getInt(R.styleable.VirtualJoystickAttr_sensitivity, 5);
		}
		
		initJoystickView(context.getAssets(), joyRad, sensitivity);
	}
	
	// =========================================
	// Initialization
	// =========================================
	private void initJoystickView(AssetManager assets, int joyRad, int sensitivity) 
	{
		setFocusable(true);
		this.joystickRad = joyRad;
		this.sensitivity = sensitivity;
		float joyFactor = 0.75f;
	
		// Initialize paints
		if(this.isInEditMode())
		{
			bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
			bgPaint.setColor(Color.LTGRAY);
			bgPaint.setStrokeWidth(1);
			bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
			bgPaint.setAlpha(150);
			
			stickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
			stickPaint.setColor(Color.DKGRAY);
			stickPaint.setStrokeWidth(1);
			stickPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		}
		else
		{
			bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
			bgPaint.setAlpha(150);
			
			stickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
			stickPaint.setColor(Color.DKGRAY);
			stickPaint.setStrokeWidth(1);
			stickPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		}
		
		// load the used bitmaps
		try
		{		
			// only load the bitmaps if we are not in edit mode
			if(!this.isInEditMode())
			{
				// load bitmaps
				background = BitmapFactory.decodeStream(assets.open("images/joystick_bg.png"));
				stick = BitmapFactory.decodeStream(assets.open("images/joystick.png"));
				
				// get bitmaps based on joystick radius
				joyFactor = (float)stick.getWidth() / (float)background.getWidth();
				background = Bitmap.createScaledBitmap(background, (int)joystickRad * 2, (int)joystickRad * 2, true);
			}
			
			// radius of the stick
			stickRad = (int)(joyFactor * (joystickRad * 1.0));
			
			// only scale the bitmap if we are not in editmode
			if( !this.isInEditMode() )
			{
				stick = Bitmap.createScaledBitmap(stick, stickRad, stickRad, true);
			}
			
			// stick radius is the half of the radius
			stickRad /= 2;
		}
		catch(IOException ex)
		{ 
			// har har
		}
	}

	// =========================================
	// Public Methods 
	// =========================================
	public void setOnJoystickMovedListener(JoystickListener listener) 
	{
		  this.listener = listener;
	}
	  
	// =========================================
	// Drawing Functionality 
	// =========================================
	@Override
	 protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld){
	     super.onSizeChanged(xNew, yNew, xOld, yOld);

			// set the joystick at the center
			joystickX = stickPressX = this.getWidth() / 2;
			joystickY = stickPressY = this.getHeight() / 2;
	}

	@Override
	protected void onDraw(Canvas canvas) 
	{
		if(!this.isInEditMode())
		{
			// Draw the background
			canvas.drawBitmap(background, (float)joystickX - (background.getWidth() / 2), (float)joystickY - (background.getHeight() / 2), bgPaint);
			
			// Draw the handle
			canvas.drawBitmap(stick, (int) stickPressX - (stick.getWidth() / 2), (int) stickPressY - (stick.getWidth() / 2), stickPaint);
		}
		else
		{
			canvas.drawCircle((int) joystickX, (int)joystickY, joystickRad, bgPaint);
			canvas.drawCircle((int) stickPressX, (int)stickPressY, stickRad, stickPaint);
		}
	}

	// =========================================
	// Event Functionality 
	// =========================================
	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		// save the touch event
		int actionType = event.getAction();
		float pX = event.getX();
		float pY = event.getY();
		
		// handle actions
		if(actionType == MotionEvent.ACTION_DOWN||released)
		{
			released = false;
			// if pressed spot is within the hotzone
			if(WithinHotZone(pX, pY))
			{
				// we set the joystick at the pressed position
				joystickX = stickPressX = pX;
				joystickY = stickPressY = pY;
				
				// redraw the view
				invalidate();
			}
		}
		else if (actionType == MotionEvent.ACTION_MOVE&&!released)
		{
			// we get the new position of the stick
			stickPressX = pX;
			stickPressY = pY;
			
			// keep in bounderies		
			double deltaX = stickPressX - joystickX;
			double deltaY = stickPressY - joystickY;
		
			// get the len
			double pressLen = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
		
			// if the stick pos distance is higher then the circle bounderies of the joystick
			if( (pressLen + stickRad) > joystickRad)
			{
				// put the stick at the joystick radius bound
				stickPressX = joystickX + deltaX / pressLen * (joystickRad - stickRad);
				stickPressY = joystickY + deltaY / pressLen * (joystickRad - stickRad);
				
				// get the new deltaX and deltaY
				deltaX = deltaX / pressLen * (joystickRad);
				deltaY = deltaY / pressLen * (joystickRad);
			}
			else
			{
				deltaX = deltaX / (joystickRad - stickRad) * joystickRad;
				deltaY = deltaY / (joystickRad - stickRad) * joystickRad;
			}

			// send event to the listener
			if (listener != null) 
			{
				float pan = (float)( deltaX / joystickRad * sensitivity );
				float tilt = (float)( -deltaY  / joystickRad * sensitivity);
				
				if(!allowX)
				{
					pan =0;
					stickPressX = joystickX;
				}
				if(!allowY)
				{
					tilt=0;
					stickPressY = joystickY;
				}
				
				listener.OnMoved(pan, tilt);
			}
			
			// redraw the view
			invalidate();
		} 
		else if (actionType == MotionEvent.ACTION_UP)
		{
			released = true;
			// animate the stick so that it moves back to the center
			returnHandleToCenter();
		
			// if we have a listener we say the joystick is released
		    if (listener != null) 
		    {
		    	listener.OnReleased();
		    }
		}
		
		return true;
	}
	
	// check whether we pressed inside the hotzone
	private boolean WithinHotZone(float x, float y)
	{
		return x < this.getWidth() - joystickRad && x > joystickRad &&
				y < this.getHeight() - joystickRad && y > joystickRad;
	}
	
	//returns the thumbstick to the center of the joystick
	private void returnHandleToCenter() 
	{
//		Handler handler = new Handler();
//		int numberOfFrames = 5;
//		final double intervalsX = (stickPressX - joystickX) / -numberOfFrames;
//		final double intervalsY = (stickPressY - joystickY) / -numberOfFrames;
//		
//		for (int i = 0; i < numberOfFrames; i++) 
//		{
//		  handler.postDelayed(new Runnable() 
//		  {
//		    public void run() 
//		    {
//		    	stickPressX += intervalsX;
//		    	stickPressY += intervalsY;
//		      invalidate();
//		    }
//		  }, i * 40);
//		}
		
		stickPressX = joystickX;
    	stickPressY = joystickY;
    	invalidate();
	}
}